import { Injectable } from '@nestjs/common';
import { DictionaryService } from '../common/dictionary/dictionary.service';
import { MongodbService } from '@app/mongodb';
import { AliIotService } from 'apps/ali-iot/src/ali-iot.service';
import { DbService } from '@app/db';
import { PageDto } from './dto';
import { Device } from '@app/db/entities/Device';
import { FindOptionsWhere, In, Like } from 'typeorm';
import { DeviceResult } from '../types/result.type';
import { Member } from '@app/db/common/entities/Member';
import { DeviceParamsRule } from '@app/db/entities/DeviceParamsRule';
import { PricingSlowCharge } from '@app/db/entities/PricingSlowCharge';
import { ChargingStation } from '@app/db/entities/ChargingStation';
import { PricingFastCharge } from '@app/db/entities/PricingFastCharge';

@Injectable()
export class DeviceService {
    constructor(
        private readonly dictionary:DictionaryService,
        private readonly mongodb:MongodbService,
        private readonly aliIot:AliIotService,
        private readonly db:DbService,
    ){}

    async pushSettingToDeviceByStation(station:ChargingStation) {
        const devices = await this.db.device.find({stationId : station.id}, ['paramsRule', 'id', 'chargeType', 'aliProduct', 'connectStatus'])
        if(devices.length == 0) return false
        const slowPricing = await this.db.pricingSlowCharge.findOne({id : station.slowPricing})
        const fastPricing = await this.db.pricingFastCharge.findOne({id : station.fastPricing})

        const paramsRuleIds:number[] = []
        const paramsRuleMap:{[key:string] : DeviceParamsRule} = {}

        devices.forEach(device => {
            if(device.paramsRule != 0 && paramsRuleIds.includes(device.paramsRule) == false) {
                paramsRuleIds.push(device.paramsRule)
            }
        })

        if(paramsRuleIds.length > 0) {
            const paramsRules = await this.db.deviceParamsRule.find({id : paramsRuleIds.length == 0 ? paramsRuleIds[0] : In(paramsRuleIds)}, ['id', 'params'])
            paramsRules.forEach(param => {
                paramsRuleMap[param.id] = param
            })
        }

        devices.forEach(device => {
            if(device.connectStatus == 0) return
            const paramsRule = paramsRuleMap[device.paramsRule]

            if(!paramsRule) return

            const msg:any = {
                type : 3,
                t : 5,
                occupy_fee : 0,
                ...paramsRule.params,
            }

            if(device.chargeType == 1) { //快充

            }else { //慢充
                msg.service_interval = 4 //当前计价时段（0:无/1:尖/2:峰/3:平/4:谷
                msg.elec_fee = Number(slowPricing.price) * 10
                msg.service_fee = Number(slowPricing.service) * 10
            }

            this.aliIot.pubToDevice(device.id, device.aliProduct, msg).then(res => {
                console.log(res)
            })
        })

    }

    async sendSettingToDevice(deviceIds:number[]) {
        const devices = await this.db.device.find({id : deviceIds.length == 0 ? deviceIds[0] : In(deviceIds)}, ['id', 'paramsRule', 'stationId', 'pricing', 'chargeType', 'aliProduct'])
        const paramsRuleIds:number[] = []
        const pricingIds:number[] = []
        const paramsRuleMap:{[key:string] : DeviceParamsRule} = {}
        const pricingMap:{[key:string] : PricingSlowCharge} = {}

        const stationIds:number[] = []

        devices.forEach(device => {
            if(device.paramsRule != 0 && paramsRuleIds.includes(device.paramsRule) == false) {
                paramsRuleIds.push(device.paramsRule)
            }

            if(device.stationId > 0 && !stationIds.includes(device.stationId)) {
                stationIds.push(device.stationId)
            }

            // if(device.pricing != 0 && pricingIds.includes(device.pricing) == false) {
            //     pricingIds.push(device.pricing)
            // }
        })

        if(stationIds.length == 0) return false
        
        const stations = await this.db.chargingStation.find({id : stationIds.length == 1 ? stationIds[0] : In(stationIds)}, ['fastPricing', 'slowPricing', 'id'])

        if(stations.length == 0) return false

        const fastPricingIds:number[] = []
        const slowPricingIds:number[] = []

        let fastPricings:PricingFastCharge[] = []
        let slowPricings:PricingSlowCharge[] = []

        stations.forEach(station => {
            if(station.fastPricing != 0 && !fastPricingIds.includes(station.fastPricing)) {
                fastPricingIds.push(station.fastPricing)
            }

            if(station.slowPricing != 0 && !slowPricingIds.includes(station.slowPricing)) {
                slowPricingIds.push(station.slowPricing)
            }
        })

        if(fastPricingIds.length > 0) {
            fastPricings = await this.db.pricingFastCharge.find({id : fastPricingIds.length == 1 ? fastPricingIds[0] : In(fastPricingIds)})
        }

        if(slowPricingIds.length > 0) {
            slowPricings = await this.db.pricingSlowCharge.find({id : slowPricingIds.length == 1 ? slowPricingIds[0] : In(slowPricingIds)})
        }
        


        // if(pricingIds.length > 0) {
        //     const pricings = await this.db.pricingSlowCharge.find({id : pricingIds.length == 0 ? pricingIds[0] : In(pricingIds)}, ['id', 'price', 'service', 'hourKwh'])
        //     pricings.forEach(pricing => {
        //         pricingMap[pricing.id] = pricing
        //     })
        // }

        if(paramsRuleIds.length > 0) {
            const paramsRules = await this.db.deviceParamsRule.find({id : paramsRuleIds.length == 0 ? paramsRuleIds[0] : In(paramsRuleIds)}, ['id', 'params'])
            paramsRules.forEach(param => {
                paramsRuleMap[param.id] = param
            })
        }

        devices.forEach(device => {
            if(device.stationId == 0) return
            const station = stations.find(item => item.id == device.stationId)
            if(!station) return

            const paramsRule = paramsRuleMap[device.paramsRule]
            const pricing = pricingMap[device.pricing]

            if(!paramsRule || !pricing) return

            const msg:any = {
                type : 3,
                t : 5,
                ...paramsRule.params,
            }

            if(device.chargeType == 1) { //快充

            }else { //慢充
                const pricing = slowPricings.find(item => item.id == station.slowPricing)
                if(!pricing) return

                msg.service_interval = 4 //当前计价时段（0:无/1:尖/2:峰/3:平/4:谷
                msg.elec_fee = Number(pricing.price) * 10
                msg.service_fee = Number(pricing.service) * 10
            }

            this.aliIot.pubToDevice(device.id, device.aliProduct, msg).then(res => {
                console.log(res)
            })
        })
    }


    async pageOfTentant(dto:PageDto, member:Member) {
        let result:PaginationResult<DeviceResult> = {
            total : 0,
            rows : []
        }

        let where:FindOptionsWhere<Device> = {
            
        }

        const query = this.db.device.createQueryBuilder()

        const owners:number[] = [member.tenantId]

        if(dto.id) {
            where.id = dto.id
        }

        if(dto.name) {
            where.name = Like('%' + dto.name + '%')
        }

        if(dto.stationId != undefined) {
            where.stationId = dto.stationId
        }


        if(dto.type && dto.type.length > 0) {
            where.type = In(dto.type)
        }


        query.where(where)

        if(dto.owner) {
            owners.push(dto.owner)
        }

        if(owners.length != 0) {
            owners.forEach(owner => {
                query.andWhere(`JSON_CONTAINS(owners, '${owner}')`)
            })
        }

        result.total = await query.getCount()

        if(result.total > 0) {
            result.rows = await query.setFindOptions({
                select : {aliId : false, aliProduct : false, aliSecret : false, portData : false, portStatus : false}
            }).skip((dto.pageNo - 1) * dto.pageSize).take(dto.pageSize).orderBy({id : 'DESC'}).getMany()
            //result.rows = await this.db.device.pagination(dto.pageNo, dto.pageSize, where, null, {id : 'DESC'})
            const typeIds:number[] = []
            const tenantIds:number[] = []

            let types:{[key:string] : string} = {}
            let tenants:{[key:string] : string} = {}

            result.rows.forEach(row => {
                if(row.owners && row.owners.length > 0) {
                    let i = row.owners.findIndex((owner) => owner == member.tenantId) + 1
                    if(row.owners[i] != undefined) {
                        row.owner = row.owners[i]
                        tenantIds.push(row.owner)
                    }
                }

                typeIds.push(row.type)
            })

            if(tenantIds.length > 0) {
                let tenantRows = await this.db.tenant.find({id : In(tenantIds)}, ['id', 'name'])
                tenantRows.forEach(t => {
                    tenants[t.id] = t.name
                })
                tenantRows = null
            }

            let typeRows = await this.queryDeviceTypes()
            typeRows.forEach(t => {
                types[t.code] = t.name
            })
            typeRows = null



            result.rows.forEach(row => {
                if(row.owner) {
                    row.ownerName = tenants[row.owner]
                    delete row.owners
                }
                row.typeName = types[row.type]
            })
        }

        return result
    }

    async page(dto:PageDto) {
        let result:PaginationResult<DeviceResult> = {
            total : 0,
            rows : []
        }

        let where:FindOptionsWhere<Device> = {
            
        }

        const query = this.db.device.createQueryBuilder()

        if(dto.id) {
            where.id = dto.id
        }

        if(dto.name) {
            where.name = Like('%' + dto.name + '%')
        }


        if(dto.type && dto.type.length > 0) {
            where.type = In(dto.type)
        }

        if(dto.connectStatus !== undefined) {
            where.connectStatus = dto.connectStatus
        }



        query.where(where)

        result.total = await query.getCount()

        if(result.total > 0) {
            query.setFindOptions({
                select : {portData : false, portStatus : false}
            })
            result.rows = await query.skip((dto.pageNo - 1) * dto.pageSize).take(dto.pageSize).orderBy({id : 'DESC'}).getMany()
            //result.rows = await this.db.device.pagination(dto.pageNo, dto.pageSize, where, null, {id : 'DESC'})

            const tenantIds:number[] = []
            const typeIds:number[] = []


            let tenants:{[key:string] : string} = {}
            let types:{[key:string] : string} = {}

            result.rows.forEach(row => {
                if(row.owners && row.owners.length > 0) {
                    row.owner = row.owners[0]
                    tenantIds.push(row.owner)
                }

                

                typeIds.push(row.type)
            })

            if(tenantIds.length > 0) {
                let tenantRows = await this.db.tenant.find({id : In(tenantIds)}, ['id', 'name'])
                tenantRows.forEach(t => {
                    tenants[t.id] = t.name
                })
                tenantRows = null
            }

            let typeRows = await this.queryDeviceTypes()
            typeRows.forEach(t => {
                types[t.code] = t.name
            })
            typeRows = null

            


            result.rows.forEach(row => {
                if(row.owner) {
                    row.ownerName = tenants[row.owner]
                }

                

                row.typeName = types[row.type]
            })
        }
        

        return result
    }

    async queryDeviceInfo(product:string, deviceId:number) {
        const res = await this.aliIot.queryDeviceInfo(product, deviceId)
        return res.statusCode == 200 && res.body ? res.body : null
    }

    async registerToAli(product:string, deviceId:number) {
        const res = await this.aliIot.registerDevice(product, deviceId)
        return res.statusCode == 200 && res.body ? res.body : null
    }

    async batchRegisterToAli(product:string, deviceIds:number[]) {
        //const BatchCheckRes = await this.
    }

    //检查阿里云
    async batchCheckDeviceNames(product:string, deviceIds:number[]) {
        return this.aliIot.batchCheckDeviceNames(product, deviceIds) 
    }

    //注册阿里云
    async batchRegisterDeviceWithApplyId(product:string, applyId:string) {
        return this.aliIot.batchRegisterDeviceWithApplyId(product, applyId)
    }

    //查询阿里云产品
    queryProductList() {
        return this.aliIot.queryProductList()
    }

    queryDeviceTypes() {
        return this.dictionary.getItemByCategory('DEVICE_TYPE')
    }

    async deviceDataRecrod(deviceId:number, portNo:number, field:number, startTime? : number, endTime? : number, pageNo?:number, pageSize?:number) {

        let result:PaginationResult<any> = {
            total : 0,
            rows : []
        }

        const key = 'data.' + portNo + '.' + field


        // for(let i = 0; i < 32; i ++) {
        //     await this.mongodb.deviceData.updateOne({deviceId : deviceId}, {$push : {[key] : {value : i, time : 1}}}, {upsert : true})
        // }


        //return []

        

        const where = {
            deviceId : deviceId,
            [key]: { $exists: true }
        }

        let filter:any = null

        if(startTime || endTime) {
            filter = {
                input: '$' + key,
                as: 'item',
            }

            if(startTime == endTime) {
                filter.cond = {
                    $eq : ['$$item.time', startTime]
                }
            }else {
                filter.cond = {
                    $and : []
                }

                if(startTime) {
                    filter.cond.$and.push({
                        $gte : ['$$item.time', startTime]
                    })
                }

                if(endTime) {
                    filter.cond.$and.push({
                        $lte : ['$$item.time', endTime]
                    })
                }
            }
        }
        
       


        const total = await this.mongodb.deviceData.findOne(
            where,
            {
                _id : 0,
                count : {
                    $size : filter ? {$filter : filter} : '$' + key
                }
            }
        )

        

        if(total && total.get('count') > 0) {
            result.total = total.get('count')

            const res = await this.mongodb.deviceData.findOne(
                where,
                {
                    _id : 0,
                    rows : {
                        $slice : filter ? [{$filter : filter}, (pageNo - 1) * pageSize, pageSize] : ['$' + key, (pageNo - 1) * pageSize, pageSize]
                    }

                }
            )

            result.rows = res.get('rows')

        }

        return result
    }


    async exportDeviceDataRecord() {
        
    }

    async exportDevicePortFieldDataRecrod(deviceId:number, portNo:number, field:number, startTime? : number, endTime? : number, pageNo?:number, pageSize?:number) {
        const key = 'data.' + portNo + '.' + field
        const where = {
            deviceId : deviceId,
            [key]: { $exists: true }
        }

        let filter:any = null

        if(startTime || endTime) {
            filter = {
                input: '$' + key,
                as: 'item',
            }

            if(startTime == endTime) {
                filter.cond = {
                    $eq : ['$$item.time', startTime]
                }
            }else {
                filter.cond = {
                    $and : []
                }

                if(startTime) {
                    filter.cond.$and.push({
                        $gte : ['$$item.time', startTime]
                    })
                }

                if(endTime) {
                    filter.cond.$and.push({
                        $lte : ['$$item.time', endTime]
                    })
                }
            }
        }


        


        const res = await this.mongodb.deviceData.findOne(
            where,
            {
                _id : 0,
                rows : {
                    $slice : filter ? [{$filter : filter}, (pageNo - 1) * pageSize, pageSize] : ['$' + key, (pageNo - 1) * pageSize, pageSize]
                }

            }
        )

        return res.get('rows')
    }
}
